iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 4
2
自我挑戰組

軟體工程漫談系列 第 6

『用註解補足程式碼易讀性?』 -- 論註解的是與非

  • 分享至 

  • xImage
  •  

這是一篇臨時追加的主題,因為在前文中,有邦友指教,聊到註解的實用性,深有感觸。同時,想到大師Robert C. Martin也在作品中不只一次強調註解的利與弊,甚至為此特留一章加以論述,於是臨時把今天的主題換成探討註解在工程師的職涯中扮演的地位,以及不當使用時可能帶來的災難

首先,我必須說,寫註解是一個非常良好的習慣,透過較口語化的簡短敘述讓閱讀的人可以更快的了解這段程式碼的邏輯用意輸出輸入與作者的思維脈絡實在是cp值很高的投資,然而,實作上,他也很有可能因為這樣的高cp值,帶來不可輕忽的災難。

這些災難,不是註解的問題,而是使用者的使用方式違背了註解的精神,有哪些呢?症狀很多,我們簡單舉一些例子:


為了註解而註解

這是工程師很常製造的問題。以前讀書時,老師為了養成我們寫註解的好習慣,都會在程式作業裡,把註解當成評分標準之一,然而卻沒有教我們該怎樣讓註解發揮輔助的效果,與程式本身相得益彰。於是,為了分數著想,我們總是在程式寫完後,草草地在不需註解的地方加上一些相當『雞肋』的註解,譬如:

// quick sort method
public int[] quickSort(int[] input) {
  // create new int array
  int[] result;
  
  // implement quick sort algorithm
  (... do some work)

  // return the sorted array
  return result
}

是不是很有既視感?這是一個相當常見的『為了註解而註解』的範例。既然函式命名已經很清楚,大家看一眼就知道他是要實作『quick sort』演算方法,上面那一行的註解就顯得相當雞肋。此外,創造新array不是這個函式的主要目的,所以創造這個動作也不需要特別說明,註解回傳值更是無聊!你根本沒有其他方法回傳演算後的結果,不是嗎?『那中間那一行總該是重點了吧?』 -- 他當然是重點,但是你這個函式既然只做一件事,而且在函式命名中已經簡短又清楚地交代了內容,你還有需要再進入主題前一行刻意昭告天下說『大家注意,我要開始實作quick sort演算法了喔!』不用吧?

於是,對於『了解這個函式』這個目的來說,上述的四個註解,都無法比函式名稱本身帶來更多資訊,豈不雞肋哉?

不與時俱進的註解

有了能提供更多好的資訊,良好的註解後,我們馬上就要面臨與程式一樣的課題:『維護』。『維護有什麼難的?』來,我們看看下面這段:

// Add user to Table "user". 
// If user does not exists, then create a new one
// If user exists, then update columns with input
// input: user
// output: N/A
// throws: SQLException
public void addUser(Connection conn, User user) throws SQLException, WrongNameException{

  (do something...)
  
}

這就很明顯了,函式一開始只需要傳入User物件,所以註解裡非常盡責的定義了input,但是後來因為某種需求,想要把連接用的Connection改為由外部傳入,而且邏輯也改了,一旦比對用戶名稱不符,就會吐出一個自訂的WrongNameException。從創建以來,函式已經做了兩次以上的功能性變更,但是註解卻依然是最初版的。這可不行,他不提供資訊,我讀讀程式碼便罷,慘的是他還給了錯誤資訊,這還得了!但是造成這種情況是為什麼呢?

原因很簡單。通常,在進度很趕時,我們光是要把功能調整好就已經很喘了,更不要提同步修改註解了。『這一段我先這樣改,可以work我再來回頭改註解』,您肯定這樣想過吧?well...
你太天真了

多少次妳寫完功能測試無誤,就這麼雙手奉上給QA測試,測完就這麼上線了?你連回頭補自動測試的時間都沒有,還管得上註解?你出道幾年了?你以為老闆在這波專案結束會給你兩三天重新審視註解?這種老闆世間少有,如果你老闆是這樣的話你應該忠心跟隨千秋萬世,沒有的話,不用難過,你只是遇到正常的老闆罷了。

所以,寫了註解就像生了老二一樣,你不可以只顧老大不顧老二不是?不要當個不負責任的父母,就像你寫了註解卻讓他不與時俱進,這樣的註解比不寫還糟糕!

『靠勢』的註解

在我自身的職業生涯中,『靠勢』的註解是最讓我詬病的了。什麼意思,看看範例吧:

public enum CustomerType {
  // VVIP
  Type1(1),
  // VIP
  Type2(2),
  // Golden Card Member
  Type3(3),
  // Ordinary Member
  Type4(4);
  
  (... some other things)
}

寫下這段程式碼的人,心中肯定認為,他提供了足夠的資訊,這段程式碼有了足夠的資訊,所以後人閱讀時一定沒有問題。嗯...也不盡然錯,為什麼?因為他的註解的確是非常清楚,我一看就知道這是VVIP,那是普通會員等等。然而,這樣的工程師,這樣的工程師就是有了註解後就開始『靠勢』,他雖然很貼心地為了閱讀者,考慮了資訊量的充足,卻沒有考慮到的是:『使用者的心情』。

如果我是一個第三方使用者,我不是你本人,也不是你同事,甚至拿到程式時已經是個封裝完成的binary檔,譬如jar檔,試問我又要和從得知,Type1是誰,Type2是誰?什麼?叫我從Maven上抓下來解譯?你乾脆留你電話給我直接問好不?

如果我把註解的內容改寫成Enum名稱,肯定會好多了吧?試試吧!你不會後悔!


所謂Clean Code的精神,就是程式本身要『言簡意賅』。我如果能用5個字解釋,就不用10個字。能用10行寫完,就不要硬加到20行。工程師應該要先想辦法透過『程式本身』釋放足夠多的資訊,並且這樣的資訊是要『從上而下』的。

回到一開始說的,註解是非常好的習慣,因為當程式真的無法提供足夠多資訊時,註解可以作為輔助。然而,她畢竟只是輔助,就跟左手一樣

左手只是輔助示意圖

然而,有了好的程式,以及好的註解,『維護』才是令人頭痛的事,有了好的程式習慣,也要有好的維護註解的習慣,這才是優秀(而非平庸)的工程師啊!

明天見!


上一篇
『這位工程師,你在公三小』-- 論命名的重要性
下一篇
我不是停止PO文,我另起了爐灶
系列文
軟體工程漫談7
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
伊恩
iT邦新手 4 級 ‧ 2017-12-10 17:34:33

Hello,
小弟參加這次鐵人賽的文章內容,跟您的文章剛好在說明相同主題
是否可以在文章內加入您的文章連結?

Kuma iT邦新手 3 級 ‧ 2017-12-10 18:53:32 檢舉

請連無妨,榮幸之至!
:)

我要留言

立即登入留言